package com.maikang.exammall.api;

import com.maikang.exammall.entity.Commission;
import com.maikang.exammall.entity.Region;
import com.maikang.exammall.entity.UserContext;
import com.maikang.exammall.entity.constant.RedisKeyConstant;
import com.maikang.exammall.entity.dto.FindAllRequest;
import com.maikang.exammall.entity.dto.PositionDTO;
import com.maikang.exammall.entity.dto.ResponseDTO;
import com.maikang.exammall.entity.dto.promote.Client;
import com.maikang.exammall.entity.dto.promote.EarningDetailDTO;
import com.maikang.exammall.entity.dto.user.LoginInputDTO;
import com.maikang.exammall.entity.dto.user.UserProfileDTO;
import com.maikang.exammall.exception.BadRequestException;
import com.maikang.exammall.repository.DictRepository;
import com.maikang.exammall.repository.RegionRepository;
import com.maikang.exammall.service.RedisService;
import com.maikang.exammall.service.UserService;
import com.maikang.exammall.util.DateUtil;
import com.maikang.exammall.util.MapUtil;
import com.maikang.exammall.util.RegionMapUtil;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.hibernate.validator.constraints.Length;
import org.springframework.data.domain.SliceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import java.util.Date;
import java.util.List;

import static com.maikang.exammall.entity.constant.RedisKeyConstant.earningSpecificationKey;
import static com.maikang.exammall.entity.constant.RedisKeyConstant.getUserPositionKeyFromUserId;

@Log4j2
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
    private final UserService service;
    private final RedisService redisService;
    private final MapUtil mapUtil;
    private final RegionMapUtil regionMapUtil;
    private final RegionRepository regionRepository;
    private final DictRepository dictRepository;


    @GetMapping("/position")
    public ResponseDTO<PositionDTO> getUserPosition() {
        Object o = redisService.get(getUserPositionKeyFromUserId(UserContext.getUserId()));
        if (o == null) return ResponseDTO.ok();
        return ResponseDTO.ok((PositionDTO)o);
    }

    @SneakyThrows
    @PostMapping("/position")
    @Transactional(rollbackFor = Exception.class)
    public ResponseDTO<PositionDTO> reportUserPosition(@Valid @RequestBody PositionDTO positionDTO) {
        PositionDTO pos;
        log.info("request body: {}", positionDTO);
        if (positionDTO.getLongitude() == null || positionDTO.getLatitude() == null) {
            if (positionDTO.getCityId()==null) {
                throw new BadRequestException("payload should contains cityId or (longitude and latitude)");
            }
            Region region = regionMapUtil.getRegionIdToRegionMap().get(positionDTO.getCityId());
            if (region == null) {
                throw new BadRequestException("there is no such a cityId {}" + positionDTO.getCityId());
            } else if (region.getCenterLatitude().equals(0.0) || region.getCenterLongitude().equals(0.0)) {
                List<Double> longLat = mapUtil.parseLongitudeLatitudeFromAddressName(positionDTO.getCityName());
                positionDTO.setLongitude(longLat.get(0));
                positionDTO.setLatitude(longLat.get(1));
                region.setCenterLongitude(longLat.get(0));
                region.setCenterLatitude(longLat.get(1));
                regionRepository.updateLongLatById(longLat.get(0), longLat.get(1), positionDTO.getCityId());
            } else {
                positionDTO.setLongitude(region.getCenterLongitude());
                positionDTO.setLatitude(region.getCenterLatitude());
            }
        }
        log.info("parseLocationFromLongitudeAndLatitude parameter: {},{}", positionDTO.getLongitude(), positionDTO.getLatitude());
        pos = mapUtil.parseLocationFromLongitudeAndLatitude(positionDTO.getLongitude(), positionDTO.getLatitude());
        mapUtil.storePositionToRedis(UserContext.getUserId(), pos);
        return ResponseDTO.ok(pos);
    }


    @ApiOperation("获取图形验证码")
    @GetMapping("/login-captcha")
    public ResponseDTO<String> createCaptchaCode() {
        return ResponseDTO.ok(service.createCaptchaCode());
    }

    @ApiOperation("获取短信验证码")
    @GetMapping("/login-sms")
    public ResponseDTO createSMSCode(@RequestParam("mobile") @Pattern(regexp = "1[3|4|5|7|8][0-9]\\d{8}", message = "手机码有误") String mobile,
                                     @RequestParam("captcha") @Length(max = 6,min = 4, message = "图形验证码长度有误") String captcha) {
        if (StringUtils.hasLength(UserContext.getUser().getMobile()) && UserContext.getUser().getMobile().equals(mobile)) {
            log.warn("bound user {} apply for a sms", UserContext::getUser);
            return ResponseDTO.badRequest("该用户已经绑定手机号码");
        }
        Object kaptcha = redisService.get(RedisKeyConstant.getUserCaptchaKeyFromUserId(UserContext.getUserId()));
        if (kaptcha == null) {
            return ResponseDTO.badRequest("图形验证码过期，请点击重新获取");
        }
        if (!kaptcha.equals(captcha)) {
            return ResponseDTO.badRequest("图形验证码有误， 请重新填写, 或重新获取再填写");
        }
        service.createSMSCode(mobile);
        return ResponseDTO.ok();
    }

    @ApiOperation("手机登录")
    @PostMapping("/login")
    public ResponseDTO<UserProfileDTO> login(@Valid @RequestBody LoginInputDTO inputDTO) {
        return ResponseDTO.ok(service.login(inputDTO));

    }

    @GetMapping("/recruit-qrcode")
    public ResponseDTO<String> recruitQrcode() {
        return ResponseDTO.ok(service.recruitQrcode());
    }

    @ApiOperation("只能修改 昵称和性别.")
    @PutMapping("/profile")
    public ResponseDTO<UserProfileDTO> save(@Valid @RequestBody UserProfileDTO userProfileDTO) {
        userProfileDTO.setId(UserContext.getUserId());
        UserProfileDTO saved = service.saveUserProfile(userProfileDTO);
        return ResponseDTO.ok(saved);
    }

    @ApiOperation("上传头像")
    @PutMapping("/profile/avatar")
    public ResponseDTO<UserProfileDTO> uploadAvatar(@RequestParam("file") MultipartFile multipartFile) {
        if (multipartFile.isEmpty() || !StringUtils.hasLength(multipartFile.getOriginalFilename())) {
            throw new BadRequestException("上传的图片不能为空");
        }
        return ResponseDTO.ok(service.uploadAvatar(multipartFile));
    }

    @GetMapping("/profile")
    public ResponseDTO<UserProfileDTO> findUserProfile() {
        return ResponseDTO.ok(service.findUserProfile());
    }


    @PostMapping("/profile/clients")
    public ResponseDTO<Iterable<Client>> myClients(@RequestBody FindAllRequest<Boolean> findAllRequest) {
        Iterable<Client> clients = service.findClients(findAllRequest.getConditions(), findAllRequest.getPageable().getPageable());
        return ResponseDTO.ok(clients);
    }

    @PostMapping("/profile/earning-details")
    public ResponseDTO<SliceImpl<EarningDetailDTO>> earningDetails(@RequestBody FindAllRequest<Date> findAllRequest) {
        Date date = findAllRequest.getConditions();
        List<Date> dates = DateUtil.naturalMonthInterval(date);
        return ResponseDTO.ok(service.findEarning(dates.get(0), dates.get(1), findAllRequest.getPageable().getPageable()));
    }

    @PostMapping("/profile/earning-withdraw")
    public ResponseDTO<Commission> withdraw(@Valid @RequestBody Commission commission) {
        return ResponseDTO.ok(service.withdraw(commission));
    }

    @GetMapping("/profile/earning-spec")
    public ResponseDTO<String> earningSpecification() {
        String spec = dictRepository.findByKey(earningSpecificationKey).getValue();
        return ResponseDTO.ok(spec);
    }
}
